package org.budo.event.multicaster;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.budo.event.BudoEvent;
import org.budo.event.BudoEventListener;
import org.budo.event.BudoEventMulticaster;

/**
 * @author lmw
 */
public abstract class AbstractEventMulticaster implements BudoEventMulticaster {
    private List<BudoEventListener> eventListeners;

    private Map<Class<?>, List<BudoEventListener>> retrieveEventListenersCache = new ConcurrentHashMap<Class<?>, List<BudoEventListener>>();

    public List<BudoEventListener> getEventListeners() {
        return eventListeners;
    }

    public void setEventListeners(List<BudoEventListener> eventListeners) {
        this.eventListeners = eventListeners;
    }

    /**
     * 找出匹配的listeners
     */
    protected List<BudoEventListener> retrieveEventListeners(BudoEvent event) {
        Class<?> eventType = event.getClass();
        List<BudoEventListener> listeners = retrieveEventListenersCache.get(eventType);
        if (null != listeners) {
            return listeners;
        }

        List<BudoEventListener> budoEventListeners = new ArrayList<BudoEventListener>();
        for (BudoEventListener budoEventListener : eventListeners) {
            if (this.isListenerSupportEvent(budoEventListener, event)) {
                budoEventListeners.add(budoEventListener);
            }
        }

        retrieveEventListenersCache.put(eventType, budoEventListeners);
        return budoEventListeners;
    }

    /**
     * 根据泛型判断lintener是否匹配event
     */
    private Boolean isListenerSupportEvent(BudoEventListener budoEventListener, BudoEvent event) {
        Type genericSuperType = budoEventListener.getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperType;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        Type actualType = actualTypeArguments[0];
        return event.getClass().isAssignableFrom((Class<?>) actualType);
    }
}